package com.sddfp.system.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sddfp.common.constant.redis.RedisConstant;
import com.sddfp.common.model.dto.*;
import com.sddfp.common.model.vo.LoginVo;
import com.sddfp.common.result.enums.impl.BusinessCode;
import com.sddfp.common.constant.user.UserConstant;
import com.sddfp.common.exception.Assert;
import com.sddfp.common.utils.JwtTemplate;
import com.sddfp.common.utils.TokenUtils;
import com.sddfp.system.entity.User;
import com.sddfp.system.entity.UserCard;
import com.sddfp.system.entity.UserDetail;

import com.sddfp.system.mapper.UserCardMapper;
import com.sddfp.system.mapper.UserDetailMapper;
import com.sddfp.system.mapper.UserMapper;
import com.sddfp.system.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author hzk
 * @since 2025-08-25
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    private final UserDetailMapper userDetailMapper;
    private final JwtTemplate jwtTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    private final UserCardMapper userCardMapper;
    private final UserMapper userMapper;

    public UserServiceImpl(UserDetailMapper userDetailMapper, JwtTemplate jwtTemplate, StringRedisTemplate stringRedisTemplate, UserCardMapper userCardMapper, UserMapper userMapper){
        this.userDetailMapper = userDetailMapper;
        this.jwtTemplate = jwtTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
        this.userCardMapper = userCardMapper;
        this.userMapper = userMapper;
    }

    @Override
    @Transactional
    public void regist(UserRegisterDto userRegisterDto) {
        //校验用户名、密码、手机号是否唯一
        Wrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
                .eq(User::getUserName, userRegisterDto.getUserName());
        User nameUser = this.getOne(wrapper);
        Assert.error(nameUser != null, BusinessCode.USER_EXISTED);

        User pwdUser = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getPwd, userRegisterDto.getPwd()));
        Assert.error(pwdUser != null, BusinessCode.USER_PASSWORD_ERROR);

        User phoneUser = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, userRegisterDto.getPhone()));
        Assert.error(phoneUser != null, "手机号已存在");

        //保存用户信息 -- 不是所以的信息都需要填写，这里只栽出来重要的几个，也是用户会填写到的信息已经必须要有的信息
        User user = new User();
        user.setUserName(userRegisterDto.getUserName());
        user.setPhone(userRegisterDto.getPhone());
        //用hutool 工具进行加密
        user.setPwd(BCrypt.hashpw(userRegisterDto.getPwd()));
        user.setUserStatus(true);
        user.setUserType(UserConstant.USER_TYPE_PERSONAL);
        this.save(user);

        //保存用户详情
        UserDetail userDetail = new UserDetail();
        userDetail.setId(user.getId());
        userDetail.setUserName(user.getUserName());
        userDetail.setPhone(user.getPhone());
        userDetail.setBorrowMoney(new BigDecimal("0"));
        //uuid默认是有横线-的，这里把生成的uuid的横线去掉；substring(0,10)，这个是截取
        userDetail.setUserCode("UC_" + UUID.randomUUID().toString().replace("-",""));
        userDetail.setCardBindStatus(false);
        userDetailMapper.insert(userDetail);
    }

    @Override
    public LoginVo login(LoginDto loginDto) {
        if (loginDto.getIsPhoneLogin()){
            //能进来就是手机号登录 手机号 + 验证码
            return userPhoneLogin(loginDto);
        }else {
            //反之就是账号 + 密码登录
            return userNamePwdLogin(loginDto);
        }
    }

    //账号登录
    private LoginVo userNamePwdLogin(LoginDto loginDto) {
        //1、校验用户是否存在
        Wrapper<User> wrapper = Wrappers.lambdaQuery(User.class).eq(User::getUserName, loginDto.getLoginName());
        User user = this.getOne(wrapper);
        Assert.error(user == null,BusinessCode.USER_LOGIN_ERROR);
        //2、校验密码是否正常
        Assert.error(!BCrypt.checkpw(loginDto.getLoginSecret(),user.getPwd()),BusinessCode.USER_LOGIN_ERROR);
        return getLoginVo(user);
    }
    private LoginVo getLoginVo(User user) {
        //3、生成token返回给前端
        Map<String,Object> payload = new HashMap<>();
        payload.put("userId", user.getId());
        // 生成token
        String token = jwtTemplate.createToken(payload);
        // 返回LoginVo
        return LoginVo.builder()
                .id(user.getId())
                .userName(user.getUserName())
                .phone(user.getPhone())
                .token(token)
                .build();
    }

    //手机号登录
    private LoginVo userPhoneLogin(LoginDto loginDto) {
        String key = RedisConstant.SMS_PREFIX + loginDto.getLoginName();
        //获取验证码
        String redisCode = stringRedisTemplate.opsForValue().get(key);
        //校验验证码是否失效
        Assert.error(redisCode == null,BusinessCode.INVALID_VERIFICATION);
        //校验验证码是否一致
        Assert.error(!redisCode.equals(loginDto.getLoginSecret()),BusinessCode.USER_VERIFY_CODE_ERROR);
        //删除验证码
        stringRedisTemplate.delete(key);

        //判断手机号是否存在
        User userdb = this.getOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getPhone, loginDto.getLoginName()));
        if (userdb == null){
            UserRegisterDto registerDto = new UserRegisterDto();
            // 手机验证码登录，手机号码不存在，自动注册，此时用户名随机生成、密码默认123456
            //registerDto.setUserName(RandomUtil.randomString(6));
            registerDto.setUserName(loginDto.getLoginName());
            registerDto.setPwd("123456");
            registerDto.setPhone(loginDto.getLoginName());

            regist(registerDto);

            // 根据手机号码查询当前用户状态，然后可以给外边写入token里传出去
            userdb = this.getOne(Wrappers.lambdaQuery(User.class)
                    .eq(User::getPhone, loginDto.getLoginName()));
        }

        //生成token返回给前端
        return getLoginVo(userdb);
    }

    /**
     * 用户绑卡：
     * 1、从token中获取userId
     * 2、校验：根据用户id查询u_user_detail用户表，根据CARD_BIND_STATUS字段判断是否已经绑卡
     * 3、如果未绑卡，保存保卡信息到u_user_card表
     * 4、更新用户表：把请求参数中的用户真实姓名、身份证号码更新到u_user表中
     * 5、生成请求流水号，用来访问托管代理，并更新u_user_detail表中流水号字段REQ_CODE
     * 6、远程访问托管代理服务（暂不实现）
     */
    @Override
    public SignatureDto addBankAccount(CardBindDto cardBindDto) {

        return null;
    }
}
